﻿using System;
using System.Collections.Generic;
using System.Linq;
using Ast.Framework.Caching;
using Ast.Framework.Environment.Extensions.Loaders;
using Ast.Framework.Environment.Extensions.Models;
using Ast.Framework.FileSystems.Paths;
using Ast.Framework.FileSystems.SiteFolders;
using Castle.Core.Logging;

namespace Ast.Framework.Environment.Extensions
{
    public interface IExtensionMonitoringCoordinator
    {
        void MonitorExtensions(Action<IVolatileToken> monitor);
    }

    public class ExtensionMonitoringCoordinator : IExtensionMonitoringCoordinator
    {
        private readonly IModuleFolderRoot _moduleFolderRoot;
        private readonly IPathMonitor _pathMonitor;
        private readonly IAsyncTokenProvider _asyncTokenProvider;
        private readonly IExtensionManager _extensionManager;
        private readonly IEnumerable<IExtensionLoader> _loaders;

        public ExtensionMonitoringCoordinator(IModuleFolderRoot moduleFolderRoot, IPathMonitor pathMonitor, IAsyncTokenProvider asyncTokenProvider, IExtensionManager extensionManager, IEnumerable<IExtensionLoader> loaders)
        {
            _moduleFolderRoot = moduleFolderRoot;
            _pathMonitor = pathMonitor;
            _asyncTokenProvider = asyncTokenProvider;
            _extensionManager = extensionManager;
            _loaders = loaders;

            Logger = NullLogger.Instance;
        }

        public ILogger Logger { get; set; }

        public bool Disabled { get; set; }

        public void MonitorExtensions(Action<IVolatileToken> monitor)
        {
            // We may be disabled by custom host configuration for performance reasons
            if (Disabled)
                return;

            //PERF: Monitor extensions asynchronously.
            monitor(_asyncTokenProvider.GetToken(MonitorExtensionsWork));
        }

        public void MonitorExtensionsWork(Action<IVolatileToken> monitor)
        {
            Logger.Info("Start monitoring extension files...");
            // Monitor add/remove of any module
            Logger.DebugFormat("Monitoring path \"{0}\"", _moduleFolderRoot.Path);
            monitor(_pathMonitor.WhenPathChanges(_moduleFolderRoot.Path));

            // Give loaders a chance to monitor any additional changes
            var extensions = _extensionManager.AvailableExtensions().Where(d => DefaultExtensionTypes.IsModule(d.ExtensionType)).ToList();
            foreach (var extension in extensions)
            {
                foreach (var loader in _loaders)
                {
                    loader.Monitor(extension, monitor);
                }
            }
            Logger.Info("Done monitoring extension files...");
        }
    }
}